/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

#import "FBSDKButton+Internal.h"

#import "FBSDKApplicationLifecycleNotifications.h"
#import <FBSDKCoreKit/FBSDKCoreKit-Swift.h>

#define HEIGHT_TO_FONT_SIZE 0.47
#define HEIGHT_TO_MARGIN 0.27
#define HEIGHT_TO_PADDING 0.23
#define HEIGHT_TO_TEXT_PADDING_CORRECTION 0.08

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

@interface FBSDKButton ()

@property (nonatomic) BOOL skipIntrinsicContentSizing;
@property (nonatomic) BOOL isExplicitlyDisabled;

@end

@implementation FBSDKButton

static id _applicationActivationNotifier;
static id<FBSDKEventLogging> _eventLogger;
static Class<FBSDKAccessTokenProviding> _accessTokenProvider;

+ (nullable id)applicationActivationNotifier
{
  return _applicationActivationNotifier;
}

+ (void)setApplicationActivationNotifier:(nullable id)applicationActivationNotifier
{
  _applicationActivationNotifier = applicationActivationNotifier;
}

+ (nullable id<FBSDKEventLogging>)eventLogger
{
  return _eventLogger;
}

+ (void)setEventLogger:(nullable id<FBSDKEventLogging>)eventLogger
{
  _eventLogger = eventLogger;
}

+ (nullable Class<FBSDKAccessTokenProviding>)accessTokenProvider
{
  return _accessTokenProvider;
}

+ (void)setAccessTokenProvider:(nullable Class<FBSDKAccessTokenProviding>)accessTokenProvider
{
  _accessTokenProvider = accessTokenProvider;
}

+ (void)configureWithApplicationActivationNotifier:(id)applicationActivationNotifier
                                       eventLogger:(id<FBSDKEventLogging>)eventLogger
                               accessTokenProvider:(Class<FBSDKAccessTokenProviding>)accessTokenProvider
{
  self.applicationActivationNotifier = applicationActivationNotifier;
  self.eventLogger = eventLogger;
  self.accessTokenProvider = accessTokenProvider;
}

#if DEBUG
+ (void)resetClassDependencies
{
  self.applicationActivationNotifier = nil;
  self.eventLogger = nil;
  self.accessTokenProvider = nil;
}

#endif

#pragma mark - Object Lifecycle

- (instancetype)initWithFrame:(CGRect)frame
{
  if ((self = [super initWithFrame:frame])) {
    _skipIntrinsicContentSizing = YES;
    [self configureButton];
    _skipIntrinsicContentSizing = NO;
  }
  return self;
}

- (void)awakeFromNib
{
  [super awakeFromNib];
  _skipIntrinsicContentSizing = YES;
  [self configureButton];
  _skipIntrinsicContentSizing = NO;

  // Workaround for blank button showing when created in a Storyboard in Xcode 13
  if ([self respondsToSelector:@selector(setConfiguration:)]) {
    [self performSelector:@selector(setConfiguration:) withObject:nil];
  }
  self.titleLabel.font = [self defaultFont];
}

#pragma mark - Properties

- (void)setEnabled:(BOOL)enabled
{
  _isExplicitlyDisabled = !enabled;
  [self checkImplicitlyDisabled];
}

#pragma mark - Layout


- (CGSize)intrinsicContentSize
{
  if (_skipIntrinsicContentSizing) {
    return CGSizeZero;
  }
  _skipIntrinsicContentSizing = YES;
  CGSize size = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
  _skipIntrinsicContentSizing = NO;
  return size;
}

- (CGSize)sizeThatFits:(CGSize)size
{
  if (self.hidden) {
    return CGSizeZero;
  }
  CGSize normalSize = [self sizeThatFits:size title:[self titleForState:UIControlStateNormal]];
  CGSize selectedSize = [self sizeThatFits:size title:[self titleForState:UIControlStateSelected]];
  return CGSizeMake(MAX(normalSize.width, selectedSize.width), MAX(normalSize.height, selectedSize.height));
}

- (void)sizeToFit
{
  CGRect bounds = self.bounds;
  bounds.size = [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
  self.bounds = bounds;
}


#pragma mark - Subclass Methods

- (void)logTapEventWithEventName:(FBSDKAppEventName)eventName
                      parameters:(nullable NSDictionary<FBSDKAppEventParameterName, id> *)parameters
{
  [self.class.eventLogger logInternalEvent:eventName
                                parameters:parameters
                        isImplicitlyLogged:YES
                               accessToken:[self.class.accessTokenProvider currentAccessToken]];
}

- (void)checkImplicitlyDisabled
{
  BOOL enabled = !_isExplicitlyDisabled && !self.implicitlyDisabled;
  BOOL currentEnabled = self.enabled;
  super.enabled = enabled;
  if (currentEnabled != enabled) {
    [self invalidateIntrinsicContentSize];
    [self setNeedsLayout];
  }
}

- (void)configureButton
{
  [self configureWithIcon:[self defaultIcon]
                    title:nil
          backgroundColor:[self defaultBackgroundColor]
         highlightedColor:[self defaultHighlightedColor]];
}

- (void)configureWithIcon:(nullable FBSDKIcon *)icon
                    title:(nullable NSString *)title
          backgroundColor:(nullable UIColor *)backgroundColor
         highlightedColor:(nullable UIColor *)highlightedColor
{
  [self _configureWithIcon:icon
                      title:title
            backgroundColor:backgroundColor
           highlightedColor:highlightedColor
              selectedTitle:nil
               selectedIcon:nil
              selectedColor:nil
   selectedHighlightedColor:nil];
}

- (void) configureWithIcon:(nullable FBSDKIcon *)icon
                     title:(nullable NSString *)title
           backgroundColor:(nullable UIColor *)backgroundColor
          highlightedColor:(nullable UIColor *)highlightedColor
             selectedTitle:(nullable NSString *)selectedTitle
              selectedIcon:(nullable FBSDKIcon *)selectedIcon
             selectedColor:(nullable UIColor *)selectedColor
  selectedHighlightedColor:(nullable UIColor *)selectedHighlightedColor
{
  [self _configureWithIcon:icon
                      title:title
            backgroundColor:backgroundColor
           highlightedColor:highlightedColor
              selectedTitle:selectedTitle
               selectedIcon:selectedIcon
              selectedColor:selectedColor
   selectedHighlightedColor:selectedHighlightedColor];
}

- (UIColor *)defaultBackgroundColor
{
  return [UIColor colorWithRed:24.0 / 255.0 green:119.0 / 255.0 blue:242.0 / 255.0 alpha:1.0];
}

- (UIColor *)defaultDisabledColor
{
  return [UIColor colorWithRed:189.0 / 255.0 green:193.0 / 255.0 blue:201.0 / 255.0 alpha:1.0];
}

- (UIFont *)defaultFont
{
  CGFloat size = 15;
  return [UIFont systemFontOfSize:size weight:UIFontWeightSemibold];
}

- (UIColor *)defaultHighlightedColor
{
  return [UIColor colorWithRed:21.0 / 255.0 green:105.0 / 255.0 blue:214.0 / 255.0 alpha:1.0];
}

- (FBSDKIcon *)defaultIcon
{
  return [FBSDKLogo new];
}

- (UIColor *)defaultSelectedColor
{
  return [self defaultBackgroundColor];
}

- (UIColor *)highlightedContentColor
{
  return [UIColor colorWithRed:218.0 / 255.0 green:221.0 / 255.0 blue:226.0 / 255.0 alpha:1.0];
}

- (BOOL)isImplicitlyDisabled
{
  return NO;
}

- (CGSize)sizeThatFits:(CGSize)size title:(NSString *)title
{
  UIFont *font = self.titleLabel.font;
  CGFloat height = [self _heightForFont:font];

  UIEdgeInsets contentEdgeInsets = self.contentEdgeInsets;
  CGRect rect = CGRectZero;
  rect.size = size;
  CGSize constrainedContentSize = UIEdgeInsetsInsetRect(rect, contentEdgeInsets).size;
  CGSize titleSize = [self textSizeForText:title font:font constrainedSize:constrainedContentSize lineBreakMode:self.titleLabel.lineBreakMode];
  CGFloat padding = [self _paddingForHeight:height];
  CGFloat textPaddingCorrection = [self _textPaddingCorrectionForHeight:height];
  CGSize contentSize = CGSizeMake(height + padding + titleSize.width - textPaddingCorrection, height);

  return CGSizeMake(
    contentEdgeInsets.left + contentSize.width + contentEdgeInsets.right,
    contentEdgeInsets.top + contentSize.height + contentEdgeInsets.bottom
  );
}

- (CGSize)textSizeForText:(NSString *)text font:(UIFont *)font constrainedSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode
{
  if (!text) {
    return CGSizeZero;
  }

  NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
  paragraphStyle.lineBreakMode = lineBreakMode;
  NSDictionary<NSString *, id> *attributes = @{
    NSFontAttributeName : font,
    NSParagraphStyleAttributeName : paragraphStyle,
  };
  NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:text attributes:attributes];
  CGSize size = [attributedString boundingRectWithSize:constrainedSize
                                               options:(NSStringDrawingUsesDeviceMetrics
                                                 | NSStringDrawingUsesLineFragmentOrigin
                                                 | NSStringDrawingUsesFontLeading)
                                               context:NULL].size;
  return CGSizeMake(ceilf(size.width), ceilf(size.height));
}

- (void)_applicationDidBecomeActiveNotification:(NSNotification *)notification
{
  [self checkImplicitlyDisabled];
}

- (UIImage *)_backgroundImageWithColor:(UIColor *)color cornerRadius:(CGFloat)cornerRadius scale:(CGFloat)scale
{
  CGFloat size = 1.0 + 2 * cornerRadius;
  UIGraphicsBeginImageContextWithOptions(CGSizeMake(size, size), NO, scale);
  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextSetFillColorWithColor(context, color.CGColor);
  CGMutablePathRef path = CGPathCreateMutable();
  CGPathMoveToPoint(path, NULL, cornerRadius + 1.0, 0.0);
  CGPathAddArcToPoint(path, NULL, size, 0.0, size, cornerRadius, cornerRadius);
  CGPathAddLineToPoint(path, NULL, size, cornerRadius + 1.0);
  CGPathAddArcToPoint(path, NULL, size, size, cornerRadius + 1.0, size, cornerRadius);
  CGPathAddLineToPoint(path, NULL, cornerRadius, size);
  CGPathAddArcToPoint(path, NULL, 0.0, size, 0.0, cornerRadius + 1.0, cornerRadius);
  CGPathAddLineToPoint(path, NULL, 0.0, cornerRadius);
  CGPathAddArcToPoint(path, NULL, 0.0, 0.0, cornerRadius, 0.0, cornerRadius);
  CGPathCloseSubpath(path);
  CGContextAddPath(context, path);
  CGPathRelease(path);
  CGContextFillPath(context);
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return [image stretchableImageWithLeftCapWidth:cornerRadius topCapHeight:cornerRadius];
}

- (void)_configureWithIcon:(FBSDKIcon *)icon
                     title:(NSString *)title
           backgroundColor:(UIColor *)backgroundColor
          highlightedColor:(UIColor *)highlightedColor
             selectedTitle:(NSString *)selectedTitle
              selectedIcon:(FBSDKIcon *)selectedIcon
             selectedColor:(UIColor *)selectedColor
  selectedHighlightedColor:(UIColor *)selectedHighlightedColor
{
  [self checkImplicitlyDisabled];

  if (!icon) {
    icon = [self defaultIcon];
  }
  if (!selectedIcon) {
    selectedIcon = [self defaultIcon];
  }
  if (!backgroundColor) {
    backgroundColor = [self defaultBackgroundColor];
  }
  if (!highlightedColor) {
    highlightedColor = [self defaultHighlightedColor];
  }
  if (!selectedColor) {
    selectedColor = [self defaultSelectedColor];
  }
  if (!selectedHighlightedColor) {
    selectedHighlightedColor = highlightedColor;
  }

  self.adjustsImageWhenDisabled = NO;
  self.adjustsImageWhenHighlighted = NO;
  self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
  self.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
  self.tintColor = UIColor.whiteColor;

  BOOL forceSizeToFit = CGRectIsEmpty(self.bounds);

  CGFloat scale = UIScreen.mainScreen.scale;
  UIImage *backgroundImage;

  backgroundImage = [self _backgroundImageWithColor:backgroundColor cornerRadius:3.0 scale:scale];
  [self setBackgroundImage:backgroundImage forState:UIControlStateNormal];

  backgroundImage = [self _backgroundImageWithColor:highlightedColor cornerRadius:3.0 scale:scale];
  [self setBackgroundImage:backgroundImage forState:UIControlStateHighlighted];

  backgroundImage = [self _backgroundImageWithColor:[self defaultDisabledColor] cornerRadius:3.0 scale:scale];
  [self setBackgroundImage:backgroundImage forState:UIControlStateDisabled];

  if (selectedColor) {
    backgroundImage = [self _backgroundImageWithColor:selectedColor cornerRadius:3.0 scale:scale];
    [self setBackgroundImage:backgroundImage forState:UIControlStateSelected];
  }

  if (selectedHighlightedColor) {
    backgroundImage = [self _backgroundImageWithColor:selectedHighlightedColor cornerRadius:3.0 scale:scale];
    [self setBackgroundImage:backgroundImage forState:UIControlStateSelected | UIControlStateHighlighted];
  }

  [self setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
  [self setTitleColor:[self highlightedContentColor] forState:UIControlStateHighlighted | UIControlStateSelected];

  [self setTitle:title forState:UIControlStateNormal];
  if (selectedTitle) {
    [self setTitle:selectedTitle forState:UIControlStateSelected];
    [self setTitle:selectedTitle forState:UIControlStateSelected | UIControlStateHighlighted];
  }

  UILabel *titleLabel = self.titleLabel;
  titleLabel.lineBreakMode = NSLineBreakByClipping;
  UIFont *font = [self defaultFont];
  titleLabel.font = font;

  CGSize imageSize = CGSizeMake(font.pointSize, font.pointSize);
  UIImage *image = [icon imageWithSize:imageSize];
  image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch];
  [self setImage:image forState:UIControlStateNormal];

  if (selectedIcon) {
    UIImage *selectedImage = [selectedIcon imageWithSize:imageSize];
    selectedImage = [selectedImage resizableImageWithCapInsets:UIEdgeInsetsZero
                                                  resizingMode:UIImageResizingModeStretch];
    [self setImage:selectedImage forState:UIControlStateSelected];
    [self setImage:selectedImage forState:UIControlStateSelected | UIControlStateHighlighted];
  }

  if (forceSizeToFit) {
    [self sizeToFit];
  }
  [NSNotificationCenter.defaultCenter addObserver:self
                                         selector:@selector(_applicationDidBecomeActiveNotification:)
                                             name:FBSDKApplicationDidBecomeActiveNotification
                                           object:self.class.applicationActivationNotifier];
}

- (CGFloat)_fontSizeForHeight:(CGFloat)height
{
  return floorf(height * HEIGHT_TO_FONT_SIZE);
}

- (CGFloat)_heightForContentRect:(CGRect)contentRect
{
  UIEdgeInsets contentEdgeInsets = self.contentEdgeInsets;
  return contentEdgeInsets.top + CGRectGetHeight(contentRect) + contentEdgeInsets.bottom;
}

- (CGFloat)_heightForFont:(UIFont *)font
{
  return floorf(font.pointSize / (1 - 2 * HEIGHT_TO_MARGIN));
}

- (CGFloat)_marginForHeight:(CGFloat)height
{
  return floorf(height * HEIGHT_TO_MARGIN);
}

- (CGFloat)_paddingForHeight:(CGFloat)height
{
  return roundf(height * HEIGHT_TO_PADDING) - [self _textPaddingCorrectionForHeight:height];
}

- (CGFloat)_textPaddingCorrectionForHeight:(CGFloat)height
{
  return floorf(height * HEIGHT_TO_TEXT_PADDING_CORRECTION);
}

#pragma clang diagnostic pop

@end
